"""
运动规划展示模块
逻辑部分
"""
import math

from PyQt5 import QtCore
from PyQt5.Qt import *
from bin.collision_warning_process import collision_proess
from Logic_map import MainCode
from bin.track_obj import Track
import configparser
import pyqtgraph as pg
import time
from bin import Graphics_animation
import numpy as np
from chufa_ui import Ui_Dialog

class Demonstration(MainCode):
    day_time = QtCore.pyqtSignal(float)

    def __init__(self):
        super(Demonstration, self).__init__()
        self.collision_point = None
        self.work = QThread()
        self.obs_sum_2 = []
        self.map_bound_2 = []
        self.pen_2 = None
        self.win_2 = None
        self.vertical_layout_2 = None
        self.track = []  # 存放轨迹类
        self.track_kind = []  # 存放轨迹图元
        self.track_path = []  # 存放轨迹路径
        self.file = []  # 存放暂时读取的文件
        self.all_num = 0  # 轨迹总数
        self.time_span = 0.0  # 视频总时常
        self.current_time = 0.0  # 当前时间
        self.min_step_time = 0.0  # 最小步长
        self.enlarge_factor = 1000  # 放大倍数
        self.double_factor = 0.1  # 快进，快退速度
        self.animation_group = QParallelAnimationGroup()  # 并行动画
        self.animation_line = QTimeLine()
        # self.animation_group = []
        self.imp_text = ''
        self.mark_line = 0  # 标记位，标记进度条是否被按住,1为pressed，0为released
        self.trigger_flag = None  # 触发标记位  0为门触发，1为TTC触发，2为距离触发， 3为速度触发
        self.custom_dialog = None # 这个是出发界面

        # 链接各函数
        self.pushButton_27.clicked.connect(self.choose_track)
        self.pushButton_28.clicked.connect(self.clear_track_inf)
        self.pushButton_37.clicked.connect(self.add_track)
        self.pushButton_30.clicked.connect(self.delete_track)
        self.pushButton_29.clicked.connect(self.clear_track)
        self.pushButton_31.clicked.connect(self.collision_warning)
        self.pushButton_33.clicked.connect(self.generate_anim)
        self.checkBox_3.stateChanged.connect(self.display_track)
        self.horizontalSlider.valueChanged.connect(self.exchange_size)  #
        self.horizontalSlider.sliderPressed.connect(self.pressed_line)
        self.horizontalSlider.sliderReleased.connect(self.release_line)
        self.horizontalSlider.setTracking(True)
        self.pushButton_36.clicked.connect(self.pause_start)
        self.pushButton_35.clicked.connect(self.fast_forward)
        self.pushButton_35.setAutoRepeat(True)
        self.pushButton_34.clicked.connect(self.retreat)
        self.pushButton_34.setAutoRepeat(True)
        self.checkBox_4.stateChanged.connect(self.display_collision_point_true)
        self.checkBox_5.clicked.connect(self.pose_enable)
        self.checkBox_6.clicked.connect(self.trigger_selection)
        self.x_doubleSpinBox.setRange(-float('inf'), float('inf'))
        self.y_doubleSpinBox.setRange(-float('inf'), float('inf'))
        self.yaw_doubleSpinBox.setRange(-float('inf'), float('inf'))
        self.men_pushButton_44.clicked.connect(self.trigger_men)
        self.juli_pushButton_46.clicked.connect(self.trigger_des)
        self.TTC_pushButton_45.clicked.connect(self.trigger_TTC)
        self.pushButton_47.clicked.connect(self.trigger_speed)#改了
        self.pushButton_44.clicked.connect(self.show_trigger_ui)


        self.init_obj_type()
        self.init_map_demonstration()
        # self.init_date_demonstration()


# 在这里打开触发参数调整值设置触发的参数值
    def show_trigger_ui(self):
        if self.custom_dialog is None:
            self.custom_dialog = trigger_ui()
            self.custom_dialog.doubleSpinBox.setRange(0, float('inf'))
            self.custom_dialog.doubleSpinBox_3.setRange(0, float('inf'))
            self.custom_dialog.doubleSpinBox_4.setRange(0, float('inf'))
            self.custom_dialog.doubleSpinBox_6.setRange(0, float(100))

            self.custom_dialog.exec_()
        else:
            self.custom_dialog.exec_()

    # 门触发
    def trigger_men(self):
        """
        Lateral Offset	触发门中心点相对于平台初始点的Y坐标
        Longitudinal Offset	触发门中心点相对于平台初始点的X坐标
        Heading Angle	相对航向角，触发门朝向相对于平台的X轴夹角
        Gate Width	触发门宽度
        Trigger Speed	触发速度，触发平台运动时的VUT速度
        """
        self.trigger_flag = 0
        self.men_pushButton_44.setDown(not self.men_pushButton_44.isDown())
        #if self.TTC_pushButton_45.setDown(not self.TTC_pushButton_45.isDown()):
        #   self.TTC_pushButton_45.click()
        #elif self.juli_pushButton_46.setDown(not self.juli_pushButton_46.isDown()):
        #    self.juli_pushButton_46.click()
        #elif self.pushButton_47.setDown(not self.pushButton_47.isDown()):
        #    self.pushButton_47.click()

    # TTC 触发
    def trigger_TTC(self):
        """
        Trigger Speed	触发速度，触发平台运动时的VUT速度
        TTC Time	VUT到碰撞点的碰撞时间
        Tech Value	VUT和ATP对齐后的偏移量
        ADTC Value	VUT和ATP到达碰撞点时，VUT距离ATP起点的距离（仅在追逐工况中存在）
        """
        self.trigger_flag = 1
        self.TTC_pushButton_45.setDown(not self.TTC_pushButton_45.isDown())
        #self.men_pushButton_44.click()
        #self.juli_pushButton_46.click()
        #self.pushButton_47.click()

# 距离触发
    def trigger_des(self):
        """
        Radius	触发半径，以目标平台为圆心
        Trigger Speed	触发速度，触发ATP运动时的VUT速度
        :return:
        """
        self.trigger_flag = 2
        self.juli_pushButton_46.setDown(not self.juli_pushButton_46.isDown())
        #self.men_pushButton_44.click()
        #self.TTC_pushButton_45.click()
        #self.pushButton_47.click()

 # 速度触发
    def trigger_speed(self):
        """
        Radius	触发半径，以目标平台为圆心
        Trigger Speed	触发速度，触发ATP运动时的VUT速度
        :return:
        """
        self.trigger_flag = 3
        self.pushButton_47.setDown(not self.pushButton_47.isDown())
        #self.TTC_pushButton_45.click()
        #self.juli_pushButton_46.click()
        #self.men_pushButton_44.click()

# 这个没有意义可以去掉
    def pose_enable(self):
        if self.checkBox_5.isChecked():
            self.x_doubleSpinBox.setEnabled(True)
            self.y_doubleSpinBox.setEnabled(True)
            self.yaw_doubleSpinBox.setEnabled(True)
        else:
            self.x_doubleSpinBox.setEnabled(False)
            self.y_doubleSpinBox.setEnabled(False)
            self.yaw_doubleSpinBox.setEnabled(False)

    def trigger_selection(self):
        if self.checkBox_6.isChecked():
            pass
        else:
            pass

    def pressed_line(self):
        if self.pushButton_36.isChecked():
            # self.animation_line.stop()
            self.animation_group.pause()
        self.mark_line = 1

    def release_line(self):
        self.mark_line = 0
        if self.pushButton_36.isChecked():
            self.animation_group.start()

    # 初始化地图幕布
    def init_map_demonstration(self):
        self.vertical_layout_2 = QVBoxLayout(self.widget_xy_2)  # 在widget位置创建垂直布局
        self.win_2 = pg.GraphicsLayoutWidget(self.widget_xy_2)  # 创建图形布局小部件
        self.win_2.setBackground((164, 208, 106))  # 定义背景颜色
        self.vertical_layout_2.addWidget(self.win_2)  # 放入垂直布局
        self.pen_2 = self.win_2.addPlot()  # 添加plot显示窗体
        self.pen_2.setAspectLocked()
        s1 = pg.ScatterPlotItem()
        s2 = pg.ScatterPlotItem()
        self.pen_2.showGrid(x=True, y=True)
        self.pen_2.setLabel(axis="left", text="<span style='font-size:16px;color:black;font-family: Arial'>y(m)</span>")
        self.pen_2.setLabel(axis="bottom",
                            text="<span style='font-size:16px;color:black;font-family: Arial'>x(m)</span>")
        myPen = pg.mkPen({'color': (0, 255, 0), 'width': 5})
        myPen2 = pg.mkPen({'color': (255, 0, 0), 'width': 5})
        self.pen_2.addItem(s1, pen=myPen, name="y2")
        self.pen_2.addItem(s2, pen=myPen2, name="y4")
        self.groupBox_3.raise_()

    # 载入地图
    def load_map_demonstration(self):
        self.pen_2.clear()
        self.draw_map(self.pen_2)
        self.draw_obs(self.pen_2)
        # # 载入地图边界
        # self.pen_2.removeItem(self.map_bound_2)
        # # 创建地图边界图元
        # pen = QPen()
        # pen.setWidth(0)
        # brush = pg.mkBrush(color=(255, 255, 255))
        # self.map_bound_2 = QGraphicsPolygonItem()
        # self.map_bound_2.setBrush(brush)
        # self.map_bound_2.setPen(pen)
        # list1 = []
        # for i in self.map.map_boundary:
        #     l1 = QPointF(i[0], i[1])
        #     list1.append(l1)
        # # print(list1)
        # map_polygon = QPolygonF(list1)
        # self.map_bound_2.setPolygon(map_polygon)
        # self.pen_2.addItem(self.map_bound_2)
        # # 载入障碍物
        # for i in self.obs_sum_2:
        #     self.pen_2.removeItem(i)
        # self.obs_sum_2.clear()
        # pen1 = QPen()
        # pen1.setWidth(0)
        # pen = pg.mkBrush(color=(0, 0, 0))
        # for ob in self.map.map_obs:
        #     if ob.obs_type == 1:
        #         ob_draw = QGraphicsPolygonItem()
        #         ob_draw.setBrush(pen)
        #         ob_draw.setPen(pen1)
        #         list1 = []
        #         for i in ob.obs_point:
        #             l1 = QPointF(i[0], i[1])
        #             list1.append(l1)
        #         obs_polygon = QPolygonF(list1)
        #         ob_draw.setPolygon(obs_polygon)
        #         self.obs_sum_2.append(ob_draw)
        #     elif ob.obs_type == 2:
        #         x1 = ob.center_circle[0] - ob.radius_circle
        #         y1 = ob.center_circle[1] - ob.radius_circle
        #         ob_draw = QGraphicsEllipseItem(x1, y1, ob.radius_circle * 2, ob.radius_circle * 2)
        #         ob_draw.setPen(pen1)
        #         ob_draw.setBrush(pen)
        #         self.obs_sum_2.append(ob_draw)
        # for i in self.obs_sum_2:
        #     self.pen_2.addItem(i)

    def load_mao(self):
        super().load_mao()
        if self.work_file:
            self.load_map_demonstration()

    # 清空展示信息
    def clear_track_inf(self):
        self.textBrowser_3.clear()

    # 选择轨迹文件
    def choose_track(self):
        fname, ftype = QFileDialog.getOpenFileNames(self, "open File", '../', 'csv(*.csv)')
        if fname:
            if not self.file:
                self.file += fname
            else:
                self.file = fname
            for i in self.file:
                self.textBrowser_4.append(str(i))

    # 清空轨迹文件
    def clear_track(self):
        self.file.clear()
        self.textBrowser_4.clear()
        self.print_track_inf(f"已清空文件暂存库")

    # 加入轨迹
    # 加入轨迹函数，可能存在bug，即没有完全载入轨迹的情况，可以重构但会增加时间复杂度
    def add_track(self):
        global time_dif
        if not len(self.file) == 0:
            for i in self.file:
                tk = Track()
                # 轨迹移动
                x = self.x_doubleSpinBox.value()
                y = self.y_doubleSpinBox.value()
                h = self.yaw_doubleSpinBox.value()
                if self.checkBox_5.isChecked():
                    tk.init_obj(i, track_type=self.comboBox.currentText(), x=x, y=y, h=h)
                else:
                    tk.init_obj(i, track_type=self.comboBox.currentText())

                # 设置触发  首先要检测两条轨迹是否有交点，如果没有交点没有办法设置触发
                if self.checkBox_6.isChecked():
                    index = self.comboBox_7.currentIndex()

                    if self.trigger_flag == 0:  # 目标是完成偏移碰撞  只有横向碰撞轨迹有交点的情况才能做
                        listx0, listy0, listyaw0 = self.track[index].get_x_y(True)  # VUT
                        listx, listy = tk.get_x_y()  # ATP
                        Offset_percent = self.custom_dialog.doubleSpinBox_6.value()  # 50是居中
                        impact_point = find_collision_points((listx0, listy0), (listx, listy))
                        if not impact_point:
                            QMessageBox.warning(self, 'Warning', '两条轨迹没有交点，无法完成侧向偏移碰撞')
                            return
                        # 检测目标物到达碰撞点需要多少时间  如果目标到达碰撞点晚于主轨迹，报错
                        nearest_point, nearest_index = find_nearest_point_and_index(impact_point[0], (listx, listy))
                        nearest_point0, nearest_index0 = find_nearest_point_and_index(impact_point[0], (listx0, listy0))
                        yaw0 = self.track[index].track[round((self.track[index].simulation_time[0] + nearest_index0 * self.track[index].step_time), 2)][3]
                        yaw = tk.track[round((tk.simulation_time[0] + nearest_index * tk.step_time), 2)][3]
                        # 航向角一律转化为【0-360】
                        yaw0 = yaw0 % 360.0
                        yaw = yaw % 360.0
                        direction_flag = point_position_on_ray([listx0[0], listy0[0]], yaw0, [listx[0], listy[0]])
                        if direction_flag == 0:
                            if 180 >= (yaw - yaw0) > 0:  # 右边
                                direction_flag = 1
                            else:
                                direction_flag = -1

                        # 利用轨迹交点判断航向角左右
                        fixeddistance0 = float(round((self.track[index].size[0]/2 + tk.size[1]/2), 2))
                        fixeddistance = float((50-Offset_percent)/100 * self.track[index].size[1])
                        # 判断一个数的±
                        direction_ = None
                        if fixeddistance > 0:
                            direction_ = 1
                        elif fixeddistance < 0:
                            direction_ = -1
                        elif int(fixeddistance) == 0:
                            direction_ = None
                        if direction_ is not None:
                            print("方向：", direction_flag, "偏移位", direction_)
                            nearest_point, nearest_index = find_fixeddistance_point_and_index(impact_point[0],
                                                                                              (listx, listy),
                                                                                              abs(fixeddistance),
                                                                                              direction_ * direction_flag)
                        nearest_point0, nearest_index0 = find_fixeddistance_point_and_index(impact_point[0],
                                                                                            (listx0, listy0),
                                                                                            abs(fixeddistance0), -1)
                        Collision_time0 = self.track[index].simulation_time[0] + nearest_index0 * self.track[
                            index].step_time
                        Collision_time = tk.simulation_time[0] + nearest_index * tk.step_time
                        if Collision_time > Collision_time0:
                            QMessageBox.warning(self, 'Warning', f'目标物晚于{self.track[index].name}到达碰撞点')
                            return

                        # 修改目标物的轨迹  当Collision_time0-Collision_time在开始运动
                        time_dif = Collision_time0-Collision_time
                    elif self.trigger_flag == 1:
                        time_dif = self.custom_dialog.doubleSpinBox_5.value()
                        pass
                    elif self.trigger_flag == 2:
                        listx0, listy0, listyaw0 = self.track[index].get_x_y(True)  # VUT
                        listx, listy = tk.get_x_y()  # ATP
                        # 距离触发当VUT到ATP的距离达到设定值的时候即刻触发ATP作动。触发判定条件是距离。
                        radius = self.custom_dialog.doubleSpinBox.value()
                        for kk in range(len(listx0)):
                            distance = distance_to_rectangle(self.track[index].size[0], self.track[index].size[1],
                                                             (listx0[kk], listy0[kk]), listyaw0[kk],
                                                             (listx[0], listy[0]))
                            print(distance)
                            if distance <= radius:
                                time_dif = kk * self.track[index].step_time + self.track[index].simulation_time[0]
                                radius = None
                                break
                        if radius is not None:
                            QMessageBox.warning(self, 'Warning', 'VUT 不会运行到该距离内')
                            return

                    elif self.trigger_flag == 3:
                        # 当VUT达到一定速度时并持续一定时间启动ATP
                        listv = self.track[index].get_v()
                        trigger_v = self.custom_dialog.doubleSpinBox_3.value()
                        duration_time = self.custom_dialog.doubleSpinBox_4.value()
                        target_index = None
                        for index_v, value in enumerate(listv):
                            if value > trigger_v:
                                target_index = index_v
                                break
                        if target_index is None:
                            QMessageBox.warning(self, 'Warning', 'VUT 不会运行到该速度')
                            return
                        time_dif = target_index * self.track[index].step_time + self.track[index].simulation_time[0] + duration_time

                    # 交点检测碰撞触发
                    else:
                        listx0, listy0, listyaw0 = self.track[index].get_x_y(True)  # VUT
                        listx, listy = tk.get_x_y()  # ATP
                        impact_point = find_collision_points((listx0, listy0), (listx, listy))
                        if not impact_point:
                            QMessageBox.warning(self, 'Warning', '两条轨迹没有交点，无法碰撞')
                            return
                        # 检测目标物到达碰撞点需要多少时间  如果目标到达碰撞点晚于主轨迹，报错
                        nearest_point, nearest_index = find_nearest_point_and_index(impact_point[0], (listx, listy))
                        nearest_point0, nearest_index0 = find_nearest_point_and_index(impact_point[0], (listx0, listy0))
                        Collision_time0 = self.track[index].simulation_time[0] + nearest_index0 * self.track[index].step_time
                        Collision_time = tk.simulation_time[0] + nearest_index * tk.step_time

                        if Collision_time > Collision_time0:
                            QMessageBox.warning(self, 'Warning', f'目标物晚于{self.track[index].name}到达碰撞点')
                            return

                        # 修改目标物的轨迹  当Collision_time0-Collision_time在开始运动
                        time_dif = Collision_time0-Collision_time
                    print(time_dif)
                    tk.read_csv(i, x, y, h, time=time_dif)
                tk.name = tk.track_type + f" {self.all_num}"
                self.track.append(tk)
                inf = f"已载入{i}轨迹文件为{tk.name}"
                self.print_track_inf(inf)
                self.comboBox_2.addItem(tk.name)
                self.comboBox_7.addItem(tk.name)
                self.generate_trajectory(self.all_num)
                self.all_num += 1
                self.label_27.setText(str(self.all_num))
                self.turnaver_time()
                self.init_animation(tk)
            self.textBrowser_4.clear()
            self.file.clear()
            self.init_line_time()

    # 初始化运动类型
    def init_obj_type(self):
        filename = './conf/target_type.conf'
        config0 = configparser.ConfigParser()
        config0.read(filename)
        date = config0.sections()
        for i in date:
            self.comboBox.addItem(i)

    # 轨迹载入栏输出
    def print_track_inf(self, inf):
        self.textBrowser_3.append(str(inf))

    # 轨迹文件选择栏输出
    def print_track_file(self, inf):
        self.textBrowser_4.setText(inf)

    # 删除轨迹对象
    def delete_track(self):
        i = self.comboBox_2.currentIndex()
        self.turnaver_time()
        self.track.pop(i)
        for q in self.track_path:
            self.pen_2.removeItem(q)
        self.track_path.pop(i)
        for q in self.track_path:
            self.pen_2.addItem(q)
        self.comboBox_2.clear()
        self.comboBox_7.clear()
        for q in self.track:
            self.comboBox_2.addItem(q.name)
            self.comboBox_7.addItem(q.name)
        self.all_num -= 1
        self.label_27.setText(str(self.all_num))
        self.animation_group.clear()
        for i in self.track_kind:
            self.pen_2.removeItem(i.pixmap_item)
        for i in self.track:
            self.init_animation(i)

    # 碰撞预警,多线程操作
    def collision_warning(self):
        self.work = collision_proess(self.track)
        self.work.signal_process.connect(self.process_bar)
        self.work.signal_text.connect(self.coll_text)
        self.work.signal_point.connect(self.display_collision_point)
        self.work.start()

    def display_collision_point(self, list_point):
        self.pen_2.removeItem(self.collision_point)
        self.collision_point = None
        brush = pg.mkBrush(color=(186, 38, 198))
        self.collision_point = pg.ScatterPlotItem(pen=None, size=10, brush=brush, symbol='s')
        self.pen_2.addItem(self.collision_point)
        for i in list_point:
            self.collision_point.addPoints([i[0]], [i[1]])

    def display_collision_point_true(self):
        if self.checkBox_4.isChecked():
            self.collision_point.setPointsVisible(True)
        else:
            self.collision_point.setPointsVisible(False)

    # 轨迹展示
    def display_track(self):
        if self.checkBox_3.isChecked():
            for i in self.track_path:
                i.setVisible(True)
        else:
            for i in self.track_path:
                i.setVisible(False)

    # 后退
    def retreat(self):
        if self.pushButton_36.isChecked():
            self.animation_group.pause()
        self.current_time -= self.double_factor
        if self.current_time < 0:
            self.current_time = 0
        self.day_time.emit(self.current_time)
        self.animation_group.setCurrentTime(int(self.current_time * 1000))
        display_time = time.strftime('%H:%M:%S', time.gmtime(int(self.current_time)))
        self.label_14.setText(display_time)
        self.horizontalSlider.setValue(int(self.current_time * self.enlarge_factor))
        if self.pushButton_36.isChecked():
            self.animation_group.start()

    # 暂停 or 开始
    def pause_start(self):
        if not self.animation_group.animationCount() == 0:
            if self.pushButton_36.isChecked():
                self.animation_group.start()
                self.animation_line.start()
            else:
                self.animation_group.pause()
                self.animation_line.stop()

    # 快进
    def fast_forward(self):
        if self.pushButton_36.isChecked():
            self.animation_group.pause()
        self.current_time += self.double_factor
        if self.current_time > self.time_span:
            self.current_time = self.time_span
        self.day_time.emit(self.current_time)
        self.animation_group.setCurrentTime(int(self.current_time * 1000))
        display_time = time.strftime('%H:%M:%S', time.gmtime(int(self.current_time)))
        self.label_14.setText(display_time)
        self.horizontalSlider.setValue(int(self.current_time * self.enlarge_factor))
        if self.pushButton_36.isChecked():
            self.animation_group.start()

    # 计算最大时长
    def turnaver_time(self):
        time0 = 0.0
        step = 1000
        for i in self.track:
            if i.simulation_time[1] > time0:
                time0 = i.simulation_time[1]
            if i.step_time < step:
                step = i.step_time
        self.time_span = time0
        self.min_step_time = step
        display_time = time.strftime('%H:%M:%S', time.gmtime(int(self.time_span)))
        self.label_15.setText(str(display_time))
        self.move_bar(0)
        self.horizontalSlider.setMinimum(0)
        self.horizontalSlider.setMaximum(int(self.time_span * self.enlarge_factor))
        self.horizontalSlider.setTickInterval(int(self.min_step_time * self.enlarge_factor))
        self.current_time = 0.0

    # 轨迹生成
    def generate_trajectory(self, t):
        for i in self.track_path:
            self.pen_2.removeItem(i)
        listx, listy = self.track[t].get_x_y()
        trajectory = pg.PlotCurveItem(listx, listy, pen=pg.mkPen(color=(0, 0, 100), width=2, symbolBrush='r'))
        self.track_path.append(trajectory)
        for i in self.track_path:
            self.pen_2.addItem(i)

    # 更新时间
    def exchange_size(self):
        if self.mark_line == 1:
            ti = self.horizontalSlider.value()
            self.current_time = ti / self.enlarge_factor
            self.day_time.emit(self.current_time)
            self.animation_group.setCurrentTime(int(self.current_time * 1000))
            # self.animation_line.setCurrentTime(int(self.current_time * 1000))
            display_time = time.strftime('%H:%M:%S', time.gmtime(int(self.current_time)))
            self.label_14.setText(display_time)

    # 根据时间设置line显示与进度条
    def exchange_time(self, time0):
        time1 = (self.animation_group.currentTime() % (self.time_span * self.enlarge_factor)) / self.enlarge_factor
        self.current_time = time1
        self.day_time.emit(self.current_time)
        display_time = time.strftime('%H:%M:%S', time.gmtime(int(self.current_time)))
        self.label_14.setText(display_time)
        self.horizontalSlider.setValue(int(self.current_time * self.enlarge_factor))

    def init_line_time(self):
        self.animation_line.setDuration(int(self.time_span * 1000))
        self.animation_line.setFrameRange(0, int(self.time_span * self.enlarge_factor))
        self.animation_line.frameChanged.connect(self.exchange_time)
        self.animation_line.setLoopCount(0)

    def init_animation(self, tk: Track()):
        obj = Graphics_animation.animation_obj(tk, self.time_span)
        self.track_kind.append(obj)
        self.pen_2.addItem(obj.pixmap_item)
        self.day_time.connect(obj.pixmap_item.get_time)
        self.animation_group.addAnimation(obj.anim_pos)
        self.animation_group.addAnimation(obj.anim_rotation)
        self.animation_group.setLoopCount(-1)

    def move_bar(self, num):
        self.horizontalSlider.setValue(int(num))

    def process_bar(self, num):
        self.progressBar.setValue(int(num))

    def coll_text(self, string):
        self.textBrowser_5.clear()
        if string == '':
            string = '--------暂无碰撞--------'
        self.textBrowser_5.append(str(string))

    def generate_anim(self):
        for i in self.track_kind:
            self.pen_2.removeItem(i.pixmap_item)
        self.track_kind.clear()
        self.animation_group.clear()
        for i in self.track:
            self.init_animation(i)


# 载入轨迹需要检测轨迹的碰撞点，两个曲线的交点
def find_collision_points(curve1, curve2, threshold=1.0):
    """
    查找由 x 和 y 坐标列表表示的两条曲线之间的碰撞点。
    参数：
    - curve1：包含两个列表的元组，表示第一条曲线的 x 和 y 坐标。
    - curve2：包含两个列表的元组，表示第二条曲线的 x 和 y 坐标。
    - threshold：距离阈值，用于判定点是否为碰撞点。
    返回：
    一个包含碰撞点的列表，每个碰撞点表示为元组 (x, y)。
    """

    x1, y1 = curve1
    x2, y2 = curve2

    collision_points = []

    # 检查曲线1和曲线2的线段之间的碰撞
    for i in range(len(x1) - 1):
        for j in range(len(x2) - 1):
            x1_start, y1_start = x1[i], y1[i]
            x1_end, y1_end = x1[i + 1], y1[i + 1]
            x2_start, y2_start = x2[j], y2[j]
            x2_end, y2_end = x2[j + 1], y2[j + 1]

            # 检查线段之间的交点
            if line_segment_intersection(x1_start, y1_start, x1_end, y1_end, x2_start, y2_start, x2_end, y2_end):
                collision_points.append(((x1_start + x1_end) / 2, (y1_start + y1_end) / 2))

    return collision_points

def line_segment_intersection(x1, y1, x2, y2, x3, y3, x4, y4):
    """
    检查两条线段是否相交。
    参数：
    - x1, y1：第一条线段的第一个端点的坐标。
    - x2, y2：第一条线段的第二个端点的坐标。
    - x3, y3：第二条线段的第一个端点的坐标。
    - x4, y4：第二条线段的第二个端点的坐标。
    返回：
    如果线段相交则返回 True，否则返回 False。
"""

    def ccw(A, B, C):
        return (C[1] - A[1]) * (B[0] - A[0]) > (B[1] - A[1]) * (C[0] - A[0])

    return ccw((x1, y1), (x3, y3), (x4, y4)) != ccw((x2, y2), (x3, y3), (x4, y4)) and \
           ccw((x1, y1), (x2, y2), (x3, y3)) != ccw((x1, y1), (x2, y2), (x4, y4))


def find_nearest_point_and_index(target_point, curve):
    """
    寻找曲线上距离目标点最近的点及其索引。
    参数：
    - target_point：元组，表示目标点的 (x, y) 坐标。
    - curve：元组，包含两个列表，表示曲线的 x 和 y 坐标。
    返回：
    一个元组 (nearest_point, index)，其中 nearest_point 是一个元组，表示曲线上距离目标点最近的点的 (x, y) 坐标，
    而 index 是该点在曲线上的索引。
    """
    x_curve, y_curve = curve

    if not x_curve or not y_curve or len(x_curve) != len(y_curve):
        return None, None

    min_distance = float('inf')

    nearest_point = None
    nearest_index = None

    for i, (x, y) in enumerate(zip(x_curve, y_curve)):
        distance = math.sqrt((target_point[0] - x)**2 + (target_point[1] - y)**2)
        if distance < min_distance:
            min_distance = distance
            nearest_point = (x, y)
            nearest_index = i

    return nearest_point, nearest_index

# 函数变体 寻找距离目标点为固定距离的点
def find_fixeddistance_point_and_index(target_point, curve, fixeddistance, direction = -1):
    """
    寻找曲线上距离目标点最近的点及其索引。
    参数：
    - target_point：元组，表示目标点的 (x, y) 坐标。
    - curve：元组，包含两个列表，表示曲线的 x 和 y 坐标。
    - fixeddistance: float, 代表固定距离
    返回：
    一个元组 (nearest_point, index)，其中 nearest_point 是一个元组，表示曲线上距离目标点最近的点的 (x, y) 坐标，
    而 index 是该点在曲线上的索引。
    """
    x_curve, y_curve = curve

    if not x_curve or not y_curve or len(x_curve) != len(y_curve):
        print("错误")
        return None, None

    nearest_point = None
    nearest_index = None

    if direction == -1:
        for i, (x, y) in enumerate(zip(x_curve, y_curve)):
            distance = math.sqrt((target_point[0] - x) ** 2 + (target_point[1] - y) ** 2)
            if distance <= fixeddistance:
                nearest_point = (x, y)
                nearest_index = i
                break
        return nearest_point, nearest_index
    if direction == 1:
        for i, (x, y) in enumerate(zip(reversed(x_curve), reversed(y_curve))):
            distance = math.sqrt((target_point[0] - x) ** 2 + (target_point[1] - y) ** 2)
            if distance <= fixeddistance:
                nearest_point = (x, y)
                nearest_index = i
                break
        return nearest_point, int(len(x_curve)-1-nearest_index)

# 这是一个计算长方形距离某一点最短距离的函数
def distance_to_rectangle(length, width, center, angle, target_point):
    def rotate_point(point, angle):
        x, y = point
        radians = math.radians(angle)
        x_rotated = x * math.cos(radians) - y * math.sin(radians)
        y_rotated = x * math.sin(radians) + y * math.cos(radians)
        return x_rotated, y_rotated

    def point_to_rectangle_distance(length, width, rotated_point):
        half_length = length / 2
        half_width = width / 2

        dx = max(0, abs(rotated_point[0]) - half_length)
        dy = max(0, abs(rotated_point[1]) - half_width)

        return math.sqrt(dx**2 + dy**2)

    rotated_target = rotate_point((target_point[0] - center[0], target_point[1] - center[1]), -angle)
    distance = point_to_rectangle_distance(length, width, rotated_target)

    return distance

def point_position_on_ray(start_point, angle_degrees, point_to_check):
    # 将角度转换为弧度
    angle_radians = math.radians(angle_degrees)

    # 射线上的另一点B的坐标
    x1 = start_point[0] + math.cos(angle_radians)
    y1 = start_point[1] + math.sin(angle_radians)

    # 向量AB
    vector_AB = (x1 - start_point[0], y1 - start_point[1])

    # 向量AP
    vector_AP = (point_to_check[0] - start_point[0], point_to_check[1] - start_point[1])

    # 计算叉积
    cross_product = vector_AB[0] * vector_AP[1] - vector_AB[1] * vector_AP[0]

    # 判断点的位置关系
    if cross_product > 0:
        return -1
    elif cross_product < 0:
        return 1
    else:
        return 0

# 这个函数用来判断一个点在一条射线左右
# 在这里设置输入的触发的界面
class trigger_ui(QDialog, Ui_Dialog):
    def __init__(self):
        super(trigger_ui, self).__init__()
        self.setupUi(self)  # 在初始化中调用 setupUi

